package com.pattern.proxy.v11;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;

/**
 * 如果想让LogProxy重用，不仅可以代理Tank,还可以代理任何其它的类型[Object]
 * 怎么办？？？
 * 本质：分离代理行为与被代理对象
 * 使用JDK的动态代理
 * <p>
 * 前面写的静态代理：是因为你知道代理的是move方法，所以代理内部调用的move方法
 * 如果是需要代理其它的，是不确定是什么方法的
 * <p>
 * 使用JDK动态代理
 * InvocationHandler使用外部类的写法
 */
public class Tank implements Movable {
    @Override
    public void move() {
        System.out.println("tank moving ................");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Tank tank = new Tank();

        // System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 或运行时添加 -Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true --- ！该方式未测试
        // 设置代理生产器生产的代理文件保存下来
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // jdk1.8及之前使用： sun.misc.ProxyGenerator.saveGeneratedFiles
        // jdk1.8之后使用： jdk.proxy.ProxyGenerator.saveGeneratedFiles

        /*
        参数解释：
            1. Tank.class.getClassLoader() 作用： 生产的代理对象使用哪个类加载器加载到内存
            2. new Class[]{Movable.class}  被代理对象实现那些接口（Tank实现的接口）
            3. new InvocationHandler() 调用处理器，指被代理对象那个方法被调用时我们怎么做处理；即Tank类的move方法

         */
        Movable m = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(),
                new Class[]{Movable.class},
                new TimeHandler(tank)
        );
        /*
            一： 此处调用了move方法
            二： m是Proxy产生的对象，实际调用的是LogHandler的invoke方法
            三： 怎么调用的呢？？？ --- v10
         */
        m.move();
    }
}

class TimeHandler implements InvocationHandler {

    /**
     * 注意： 在v9版本中成员变量是Tank
     */
    private Movable m;

    public TimeHandler(Movable m) {
        this.m = m;
    }

    private void before(){
        System.out.println("TimeHandler.before");
    }

    private void after(){
        System.out.println("TimeHandler.after");
    }

    /**
     *
     * @param proxy 生产代理类对象 【在生产的代理对象语句为：　super.h.invoke(this, m3, (Object[])null);　this就是生产的代理类】
     * @param method Method类， 调用的时那个方法
     * @param args 调用方法的参数
     *
     *             参数proxy暂时没用使用，但是可能会用到它的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        String name = proxy.getClass().getName(); // 示例调用proxy对象的方法
        System.out.println("name : " + name);
        Object o = method.invoke(m, args);  // 调用m的move方法
        after();
        return o;
    }
}

interface Movable {
    void move();
}
